/**
 * @copyright Copyright (c) 2024 COOREAL. All rights reserved.
 * @author xuezejun @date 2025-06-23
 * @description 提供一个 SignalR 服务。
 */

import { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr';

/**
 * 提供一个 SignalR 服务
 */
export class SignalRService {
  /** 定义了 SignalR 连接的当前状态 */
  public isConnected: boolean = false;
  /** 定义了 SignalR 连接的错误信息 */
  public error: string | null = null;
  /** 定义了 SignalR 连接 */
  private connection: HubConnection;
  /** 定义了 SignalR 服务器 URL */
  private serverUrl: string;

  /**
   * 构造函数，初始化 SignalR 服务
   * @param serverUrl SignalR 服务器 URL
   */
  constructor(serverUrl: string) {
    this.serverUrl = serverUrl;
    this.connection = this.createConnection();
  }

  /**
   * 提供一个方法用于创建 SignalR 连接。
   * @returns 新创建的 HubConnection 实例。
   */
  private createConnection(): HubConnection {
    var conn = new HubConnectionBuilder()
      .withUrl(this.serverUrl)
      .withAutomaticReconnect({
        nextRetryDelayInMilliseconds: () => {
          return 10000; // 每10秒自动重连一次
        },
      })
      .configureLogging(LogLevel.Information)
      .build();

    // 设置连接状态监听
    conn.onclose((error) => {
      this.isConnected = false;
      this.error = error?.message || 'Connection closed';
      console.error('SignalR connection closed:', error);
    });

    conn.onreconnecting((error) => {
      this.isConnected = false;
      this.error = error?.message || 'Reconnecting...';
      console.warn('SignalR reconnecting:', error);
    });

    conn.onreconnected((connectionId) => {
      this.isConnected = true;
      this.error = null;
      console.info('SignalR reconnected with connectionId:', connectionId);
    });

    return conn;
  }

  /**
   * 提供一个方法用于启动 SignalR 连接。
   * @returns 一个 Promise，在连接成功时解析，连接失败时拒绝。
   */
  public async start() {
    if (!this.connection || this.isConnected) return;

    try {
      await this.connection.start();
      this.isConnected = true;
      this.error = null;
      console.info('SignalR connection established');
    } catch (error: any) {
      this.isConnected = false;
      this.error = error.message;
      console.error('Failed to start SignalR connection:', error);

      // 可以在这里实现自定义的重连逻辑
      setTimeout(() => this.start(), 5000);
    }
  }

  /**
   * 提供一个方法用于停止 SignalR 连接。
   * @returns 一个 Promise，在连接成功时解析，连接失败时拒绝。
   */
  public async stop() {
    if (!this.connection || !this.isConnected) return;

    try {
      await this.connection.stop();
      this.isConnected = false;
      this.error = null;
      console.info('SignalR connection stopped');
    } catch (error: any) {
      this.error = error.message;
      console.error('Failed to stop SignalR connection:', error);
    }
  }

  // 注册服务器方法
  public on(methodName: string, callback: (...args: any[]) => void) {
    if (!this.connection) return;

    this.connection.on(methodName, callback);
    console.info(`Registered handler for method: ${methodName}`);
  }

  // 移除服务器方法注册
  public off(methodName: string, callback?: (...args: any[]) => void) {
    if (!this.connection) return;

    if (callback) {
      this.connection.off(methodName, callback);
    } else {
      this.connection.off(methodName);
    }
    console.info(`Unregistered handler for method: ${methodName}`);
  }

  /**
   * 提供一个方法用于调用服务器方法。
   * @param methodName 要调用的服务器方法的名称。
   * @param args 调用方法时传递的参数。
   * @returns 一个 Promise，在方法调用成功时解析，调用失败时拒绝。
   */
  public async invoke<T = void>(methodName: string, ...args: any[]): Promise<T> {
    if (!this.connection || !this.isConnected) {
      throw new Error('Cannot invoke method: Connection is not established');
    }

    try {
      console.debug(`Invoking server method: ${methodName}`);
      return await this.connection.invoke(methodName, ...args);
    } catch (error: any) {
      this.error = error.message;
      console.error(`Failed to invoke method ${methodName}:`, error);
      throw error;
    }
  }

  // 发送消息到服务器（不等待响应）
  public send(methodName: string, ...args: any[]): void {
    if (!this.connection || !this.isConnected) {
      throw new Error('Cannot send message: Connection is not established');
    }

    try {
      console.debug(`Sending message to server: ${methodName}`);
      this.connection.send(methodName, ...args);
    } catch (error: any) {
      this.error = error.message;
      console.error(`Failed to send message ${methodName}:`, error);
      throw error;
    }
  }
}
